home *** CD-ROM | disk | FTP | other *** search
- /*
- * Init A System-V Init Clone.
- *
- * Usage: /sbin/init
- * init [0123456SsQqAaBbCc]
- * telinit [0123456SsQqAaBbCc]
- *
- * Version: @(#)init.c 2.64 28-Jun-1996 MvS
- *
- * This file is part of the sysvinit suite,
- * Copyright 1991-1996 Miquel van Smoorenburg.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- */
-
- /* Standard configuration */
- #define SYSLOG 1 /* Use syslog for messages */
- #define CHANGE_WAIT 0 /* Do not change runlevel while
- waiting for a process to exit */
- #define NEWINIT 1 /* Enable use of /dev/initctl */
-
- /* Debug and test modes */
- #define DEBUG 0 /* Debug code off */
- #define INITDEBUG 0 /* Fork at startup so we can debug init. */
- #define CLEAN_UTMP 1 /* Do garbage collect in utmp*/
- #undef ROOTFS "/dev/hda2" /* Root fs to use */
-
- /* Some constants */
- #define INITPID 1 /* pid of first process */
- #define PIPE_FD 10 /* Fileno of initfifo. */
-
- /* Failsafe configuration */
- #define MAXSPAWN 10 /* Max times respawned in.. */
- #define TESTTIME 120 /* this much seconds */
- #define SLEEPTIME 300 /* Disable time */
-
- /* Default path inherited by every child if it's not set. */
- #define PATH_DFL "PATH=/bin:/sbin:/usr/bin:/usr/sbin"
-
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <sys/ioctl.h>
- #include <sys/wait.h>
- #include <sys/kd.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <errno.h>
- #include <stdio.h>
- #include <time.h>
- #include <fcntl.h>
- #include <string.h>
- #include <signal.h>
- #include <termios.h>
- #include <utmp.h>
- #include <ctype.h>
- #include <stdarg.h>
- #if SYSLOG
- # include <sys/syslog.h>
- #endif
- #if NEWINIT
- # include <sys/time.h>
- # include "initreq.h"
- #endif
- #include "paths.h"
-
- #ifndef NO_PROCESS
- # define NO_PROCESS 0
- #endif
- #ifndef SIGPWR
- # define SIGPWR SIGUSR1
- #endif
-
- /* Set a signal handler. */
- #define SETSIG(sa, sig, fun) sa.sa_handler = fun; \
- sa.sa_flags = SA_RESTART; \
- sigaction(sig, &sa, NULL);
-
- /* Version information */
- char *Version = "@(#) init 2.64 28-Jun-1996 MvS";
- char *bootmsg = "version 2.64 booting";
- #define E_VERSION "INIT_VERSION=sysvinit-2.64"
-
- /* Information about a process in the in-core inittab */
- typedef struct _child_ {
- int flags; /* Status of this entry */
- int exstat; /* Exit status of process */
- int pid; /* Pid of this process */
- time_t tm; /* When respawned last */
- int count; /* Times respawned in the last 2 minutes */
- char id[8]; /* Inittab id (must be unique) */
- char rlevel[12]; /* run levels */
- int action; /* what to do (see list below) */
- char process[128]; /* The command line */
- struct _child_ *new; /* New entry (after inittab re-read) */
- struct _child_ *next; /* For the linked list */
- } CHILD;
-
- CHILD *family = NULL; /* The linked list of all entries */
- CHILD *newFamily = NULL; /* The list after inittab re-read */
-
- CHILD ch_emerg = { /* Emergency shell */
- 0, 0, 0, 0, 0,
- "~~",
- "S",
- 3,
- "/sbin/sulogin",
- NULL,
- NULL
- };
-
- char runlevel = 'S'; /* The current run level */
- char thislevel = 'S'; /* The current runlevel */
- char prevlevel = 'N'; /* Previous runlevel */
- int dflLevel = 0; /* Default runlevel */
- int got_cont = 0; /* Set if we received the SIGCONT signal */
- int emerg_shell = 0; /* Start emergency shell? */
- sigset_t got_signals; /* Set if we received a signal. */
- int wroteReboot = 1; /* Set when we wrote the reboot record */
- int sltime = 5; /* Sleep time between TERM and KILL */
- char *argv0; /* First arguments; show up in ps listing */
- int maxproclen; /* Maximal length of argv[0] */
- struct utmp utproto; /* Only used for sizeof(utproto.ut_id) */
- char *console_dev = CONSOLE; /* Console device. */
- int wroteiosave = 0; /* Did we write ioctl.save yet? */
-
- /* Prototypes. */
- void Wtmp(char *user, char *id, int pid, int type, char *line);
- void WtmpOnly(char *user, char *id, int pid, int type, char *line);
- void Log(int loglevel, char *fmt, ...);
- void SetTerm(int how);
- void print(char *fmt);
-
- /* Actions to be taken by init */
- #define RESPAWN 1
- #define WAIT 2
- #define ONCE 3
- #define BOOT 4
- #define BOOTWAIT 5
- #define POWERFAIL 6
- #define POWERWAIT 7
- #define POWEROKWAIT 8
- #define CTRLALTDEL 9
- #define OFF 10
- #define ONDEMAND 11
- #define INITDEFAULT 12
- #define SYSINIT 13
- #define POWERFAILNOW 14
- #define KBREQUEST 15
-
- /* Macro to see if this is a special action */
- #define ISPOWER(i) ((i) == POWERWAIT || (i) == POWERFAIL || \
- (i) == POWEROKWAIT || (i) == POWERFAILNOW || \
- (i) == CTRLALTDEL)
-
- /* Values for the 'flags' field */
- #define RUNNING 2 /* Process is still running */
- #define KILLME 4 /* Kill this process */
- #define DEMAND 8 /* "runlevels" a b c */
- #define FAILING 16 /* process respawns rapidly */
- #define WAITING 32 /* We're waiting for this process */
- #define ZOMBIE 64 /* This process is already dead */
- #define XECUTED 128 /* Set if spawned once or more times */
-
- /* Log levels. */
- #define L_CO 1 /* Log on the console. */
- #define L_SY 2 /* Log with syslog() */
- #define L_VB (L_CO|L_SY) /* Log with both. */
-
- /* ascii values for the `action' field. */
- struct actions {
- char *name;
- int act;
- } actions[] = {
- { "respawn", RESPAWN },
- { "wait", WAIT },
- { "once", ONCE },
- { "boot", BOOT },
- { "bootwait", BOOTWAIT },
- { "powerfail", POWERFAIL },
- { "powerfailnow",POWERFAILNOW },
- { "powerwait", POWERWAIT },
- { "powerokwait", POWEROKWAIT },
- { "ctrlaltdel", CTRLALTDEL },
- { "off", OFF },
- { "ondemand", ONDEMAND },
- { "initdefault", INITDEFAULT },
- { "sysinit", SYSINIT },
- { "kbrequest", KBREQUEST },
- { NULL, 0 },
- };
-
- /*
- * Set the process title. We do not check for overflow of
- * the stack space since we know there is plenty for
- * our needs and we'll never use more than 10 bytes anyway.
- */
- int setproctitle(char *fmt, ...)
- {
- va_list ap;
- int len;
- char buf[256];
-
- buf[0] = 0;
-
- va_start(ap, fmt);
- len = vsprintf(buf, fmt, ap);
- va_end(ap);
-
- memset(argv0, 0, maxproclen + 1);
- strncpy(argv0, buf, maxproclen);
-
- return len;
- }
-
- /* Open a device with retries. */
- int serial_open(char *dev, int mode)
- {
- int f, fd;
- int m;
-
- /* Open device in nonblocking mode. */
- m = mode | O_NONBLOCK;
-
- /* Retry the open five times. */
- for(f = 0;f < 5; f++)
- if ((fd = open(dev, m)) >= 0) break;
- if (fd < 0) return(fd);
-
- /* Set original flags. */
- if (m != mode)
- fcntl(fd, F_SETFL, mode);
- return(fd);
- }
-
- /* We got a signal (HUP PWR WINCH ALRM INT) */
- void signal_handler(int sig)
- {
- sigaddset(&got_signals, sig);
- }
-
- /* SIGCHLD: one of our children has died. */
- void chld_handler()
- {
- CHILD *ch;
- int pid, st;
-
- /* Find out which process(es) this was (were) */
- while((pid = waitpid(-1, &st, WNOHANG)) != 0) {
- if (errno == ECHILD) break;
- #ifdef __alpha__
- /* Buggy library */
- if (pid == -ECHILD) break;
- #endif
- for( ch = family; ch; ch = ch->next )
- if ( ch->pid == pid && (ch->flags & RUNNING) ) {
- #if DEBUG
- Log(L_VB, "chld_handler: marked %d as zombie", ch->pid);
- #endif
- sigaddset(&got_signals, SIGCHLD);
- ch->exstat = st;
- ch->flags |= ZOMBIE;
- if (ch->new) {
- ch->new->exstat = st;
- ch->new->flags |= ZOMBIE;
- }
- break;
- }
- #if DEBUG
- if (ch == NULL)
- Log(L_VB, "chld_handler: unknown child %d exited.", pid);
- #endif
- }
- }
-
- /*
- * Linux ignores all signals sent to init when the
- * SIG_DFL handler is installed. Therefore we must catch SIGTSTP
- * and SIGCONT, or else they won't work....
- *
- * The SIGCONT handler
- */
- void cont_handler()
- {
- got_cont = 1;
- }
-
- /*
- * OOPS: segmentation violation!
- */
- void segv_handler()
- {
- Log(L_VB, "PANIC: segmentation violation! giving up..");
- while(1) pause();
- }
-
- /*
- * The SIGSTOP & SIGTSTP handler
- */
- void stop_handler()
- {
- got_cont = 0;
- while(!got_cont) pause();
- got_cont = 0;
- }
-
- /* Set terminal settings to reasonable defaults */
- void SetTerm(int how)
- {
- struct termios tty;
- int fd, fd2;
- int restore_ok = 0;
-
- if ((fd = serial_open(console_dev, O_RDWR|O_NOCTTY)) < 0) {
- Log(L_VB, "can't open %s", console_dev);
- return;
- }
-
- /* Do we want to save or restore modes. */
- if (how == 0) {
- /* Restore. */
- if (wroteiosave && (fd2 = open(IOSAVE, O_RDONLY)) >= 0) {
- if (read(fd2, &tty, sizeof(tty)) == sizeof(tty))
- restore_ok = 1;
- close(fd2);
- }
- if (restore_ok == 0) {
- /* Get old settings */
- (void) ioctl(fd, TCGETS, &tty);
-
- /* Set speed etc. */
- tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
- tty.c_cflag |= HUPCL|CLOCAL;
- /* FIXME: should we set C_LINE here? */
-
- /* Set the most important characters */
- tty.c_cc[VINTR] = 3;
- tty.c_cc[VQUIT] = 28;
- tty.c_cc[VERASE] = 127;
- tty.c_cc[VKILL] = 24;
- tty.c_cc[VEOF] = 4;
- tty.c_cc[VTIME] = 0;
- tty.c_cc[VMIN] = 1;
- tty.c_cc[VSTART] = 17;
- tty.c_cc[VSTOP] = 19;
- tty.c_cc[VSUSP] = 26;
- }
-
- /* Set pre and post processing */
- tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY;
- tty.c_oflag = OPOST|ONLCR;
- tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE;
-
- /* Now set the terminal line. */
- (void) ioctl(fd, TCSETS, &tty);
- (void) close(fd);
- return;
- }
-
- /* Save the terminal settings. */
- (void) ioctl(fd, TCGETS, &tty);
- if ((fd2 = open(IOSAVE, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
- if (errno != EROFS)
- Log(L_VB, "can't open(%s, O_WRONLY): %s", IOSAVE, sys_errlist[errno]);
- (void) close(fd);
- (void) close(fd2);
- return;
- }
- (void) write(fd2, &tty, sizeof(struct termios));
- wroteiosave = 1;
- (void) close(fd);
- (void) close(fd2);
- }
-
- /* Print to the system console */
- void print(char *s)
- {
- int fd;
-
- if ((fd = serial_open(console_dev, O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
- write(fd, s, strlen(s));
- close(fd);
- }
- }
-
- /* Sleep a number of seconds */
- void Sleep(int sec)
- {
- int oldAlrm; /* Previous value of timer */
- int f; /* Counter */
- int ga = sigismember(&got_signals, SIGALRM); /* Remember got_alrm flag */
-
- /* Save old alarm time */
- oldAlrm = alarm(0);
-
- /* Wait 'sec' seconds */
- for(f = 0; f < sec; f++) {
- sigdelset(&got_signals, SIGALRM);
- alarm(1);
- while(sigismember(&got_signals, SIGALRM) == 0) pause();
- }
-
- /* Reset old values of got_alrm flag and the timer */
- ga ? sigaddset(&got_signals, SIGALRM) : sigdelset(&got_signals, SIGALRM);
- if (oldAlrm) alarm(oldAlrm - sec > 0 ? oldAlrm - sec : 1);
- }
-
- /* Log something to the user */
- void Log(int loglevel, char *s, ...)
- {
- va_list va_alist;
- char buf[256];
-
- va_start(va_alist, s);
- vsprintf(buf, s, va_alist);
- va_end(va_alist);
-
- #if SYSLOG
- if (loglevel & L_SY) {
- /* Re-etablish connection with syslogd every time. */
- openlog("init", 0, LOG_DAEMON);
- syslog(LOG_INFO, buf);
- /* closelog(); NOT needed with recent libc's. */
- }
- #endif
-
- /* And log to the console. */
- if (loglevel & L_CO) {
- print("\rINIT: ");
- print(buf);
- print("\r\n");
- }
- }
-
- /* See if one character of s2 is in s1 */
- int any(char *s1, char *s2)
- {
- while(*s2)
- if (strchr(s1, *s2++) != NULL) return(1);
- return(0);
- }
-
-
- /*
- * Fork and execute.
- */
- int Spawn(CHILD *ch)
- {
- char *args[16]; /* Argv array */
- char buf[136]; /* Line buffer */
- int f, pid; /* Scratch variables */
- char *ptr; /* Ditto */
- time_t t; /* System time */
- int oldAlarm; /* Previous alarm value */
- char *proc = ch->process; /* Command line */
- char i_lvl[] = "RUNLEVEL=x"; /* Runlevel in environment. */
- char i_prev[] = "PREVLEVEL=x";/* Previous runlevel. */
- char i_cons[32]; /* console device. */
-
- /* Skip '+' if it's there */
- if (proc[0] == '+') proc++;
-
- ch->flags |= XECUTED;
-
- if (ch->action == RESPAWN || ch->action == ONDEMAND) {
- /* Is the date stamp from less than 2 minutes ago? */
- time(&t);
- if (ch->tm + TESTTIME > t)
- ch->count++;
- else
- ch->count = 0;
- ch->tm = t;
-
- /* Do we try to respawn too fast? */
- if (ch->count >= MAXSPAWN) {
-
- Log(L_VB, "Id \"%s\" respawning too fast: disabled for %d minutes",
- ch->id, SLEEPTIME / 60);
- ch->flags &= ~RUNNING;
- ch->flags |= FAILING;
-
- /* Remember the time we stopped */
- ch->tm = t;
-
- /* Try again in 5 minutes */
- oldAlarm = alarm(0);
- if (oldAlarm > SLEEPTIME || oldAlarm <= 0) oldAlarm = SLEEPTIME;
- alarm(oldAlarm);
- return(-1);
- }
- }
-
- /* See if there is an "initscript" (except in single user mode). */
- if (access(INITSCRIPT, R_OK) == 0 && runlevel != 'S') {
- /* Build command line using "initscript" */
- args[1] = SHELL;
- args[2] = INITSCRIPT;
- args[3] = ch->id;
- args[4] = ch->rlevel;
- args[5] = "unknown";
- for(f = 0; actions[f].name; f++) {
- if (ch->action == actions[f].act) {
- args[5] = actions[f].name;
- break;
- }
- }
- args[6] = proc;
- args[7] = NULL;
- } else if (any(proc, "~`!$^&*()=|\\{}[];\"'<>?")) {
- /* See if we need to fire off a shell for this command */
- /* Give command line to shell */
- args[1] = SHELL;
- args[2] = "-c";
- strcpy(buf, "exec ");
- strcat(buf, proc);
- args[3] = buf;
- args[4] = NULL;
- } else {
- /* Split up command line arguments */
- strcpy(buf, proc);
- ptr = buf;
- for(f = 1; f < 15; f++) {
- /* Skip white space */
- while(*ptr == ' ' || *ptr == '\t') ptr++;
- args[f] = ptr;
-
- /* May be trailing space.. */
- if (*ptr == 0) break;
-
- /* Skip this `word' */
- while(*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#')
- ptr++;
-
- /* If end-of-line, break */
- if (*ptr == '#' || *ptr == 0) {
- f++;
- *ptr = 0;
- break;
- }
- /* End word with \0 and continue */
- *ptr++ = 0;
- }
- args[f] = NULL;
- }
- args[0] = args[1];
- while(1) {
- if ((pid = fork()) == 0) {
- close(PIPE_FD);
- #if DEBUG && !SYSLOG
- pid = open("/dev/null", O_RDONLY);
- close(pid);
- if (pid != 0)
- Log(L_VB, "some fd is still open (%d)", pid);
- #endif
- /* Now set RUNLEVEL and PREVLEVEL */
- sprintf(i_cons, "CONSOLE=%s", console_dev);
- i_lvl[9] = thislevel;
- i_prev[10] = prevlevel;
- putenv(i_lvl);
- putenv(i_prev);
- putenv(i_cons);
- putenv(E_VERSION);
-
- /* The single user entry needs to talk to the console */
- if (strcmp(ch->rlevel, "S") == 0) {
- setsid();
- f = serial_open(console_dev, O_RDWR);
- ioctl(f, TIOCSCTTY, (void *)1);
- dup(f);
- dup(f);
- /* Set ioctl settings to default ones */
- SetTerm(0);
- } else {
- if ((f = serial_open(console_dev, O_RDWR|O_NOCTTY)) < 0) {
- Log(L_VB, "open(%s): %s", console_dev,
- sys_errlist[errno]);
- f = open("/dev/null", O_RDWR);
- }
- dup(f);
- dup(f);
- setsid();
- }
- /* Reset all the signals */
- for(f = 1; f < _NSIG; f++) signal(f, SIG_DFL);
- execvp(args[1], args + 1);
- /* Is this a bug in execvp? It does _not_ execute shell
- * scripts (/etc/rc !), so we try again with 'sh -c exec ...'
- */
- if (errno == ENOEXEC) {
- args[1] = SHELL;
- args[2] = "-c";
- strcpy(buf, "exec ");
- strcat(buf, proc);
- args[3] = buf;
- args[4] = NULL;
- execvp(args[1], args + 1);
- }
- Log(L_VB, "cannot execute \"%s\"", args[1]);
- exit(1);
- }
- #if DEBUG
- Log(L_VB, "Started id %s (pid %d)", ch->id, pid);
- #endif
-
- if (pid == -1) {
- Log(L_VB, "cannot fork, retry..", NULL, NULL);
- Sleep(5);
- continue;
- }
- return(pid);
- }
- }
-
- /* Start a child running! */
- void StartUp(CHILD *ch)
- {
- /* See if it's disabled */
- if (ch->flags & FAILING) return;
-
- switch(ch->action) {
-
- case SYSINIT:
- case BOOTWAIT:
- case WAIT:
- case POWERWAIT:
- case POWERFAILNOW:
- case POWEROKWAIT:
- case CTRLALTDEL:
- if (!(ch->flags & XECUTED)) ch->flags |= WAITING;
- case KBREQUEST:
- case BOOT:
- case POWERFAIL:
- case ONCE:
- if (ch->flags & XECUTED) break;
- case ONDEMAND:
- case RESPAWN:
- ch->flags |= RUNNING;
- if ((ch->pid = Spawn(ch)) < 0) break;
- /* Do NOT log if process field starts with '+' */
- if (ch->process[0] != '+')
- Wtmp("", ch->id, ch->pid, INIT_PROCESS, "");
- break;
- }
- }
-
- /* My version of strtok(3) */
- char *getPart(char *str, int tok)
- {
- static char *s;
- char *p, *q;
-
- if (str != NULL) s = str;
- if (s == NULL || *s == 0) return(NULL);
- q = p = s;
- while(*p != tok && *p) p++;
- if (*p == tok) *p++ = 0;
- s = p;
- return(q);
- }
-
- /* Read the inittab file. */
- void ReadItab(void)
- {
- FILE *fp; /* The INITTAB file */
- char buf[256]; /* Line buffer */
- char err[64]; /* Error message. */
- char *id, *rlevel,
- *action, *process; /* Fields of a line */
- CHILD *ch, *old, *i; /* Pointers to CHILD structure */
- CHILD *head = NULL; /* Head of linked list */
- int lineNo = 0; /* Line number in INITTAB file */
- int actionNo; /* Decoded action field */
- int f; /* Counter */
- int round; /* round 0 for SIGTERM, round 1 for SIGKILL */
- int foundOne = 0; /* No killing no sleep */
- int talk; /* Talk to the user */
- int done = 0; /* Ready yet? */
- sigset_t omask; /* For blocking SIGCHLD. */
- struct stat st; /* To stat INITLVL */
-
- #if DEBUG
- if (newFamily != NULL) {
- Log(L_VB, "PANIC newFamily != NULL");
- exit(1);
- }
- Log(L_VB, "Reading inittab");
- #endif
-
- /* Open INITTAB */
- if ((fp = fopen(INITTAB, "r")) == NULL)
- Log(L_VB, "No inittab file found");
-
- /* Read INITTAB line by line */
- while(!done) {
- /* Add single user shell entry as last one. */
- if (fp == NULL || fgets(buf, 255, fp) == NULL) {
- done = 1;
- /* See if we have a single user entry. */
- for(old = newFamily; old; old = old->next)
- if (strcmp(old->rlevel, "S") == 0) break;
- if (old == NULL)
- sprintf(buf, "~~:S:wait:%s\n", SHELL);
- else
- continue;
- }
- lineNo++;
- /* Skip comments and empty lines */
- if (buf[0] == '#' || buf[0] == '\n') continue;
-
- /* Decode the fields */
- id = getPart(buf, ':');
- rlevel = getPart(NULL, ':');
- action = getPart(NULL, ':');
- process = getPart(NULL, '\n');
-
- /* Check if syntax is OK. Be very verbose here, to
- * avoid newbie postings on comp.os.linux.setup :)
- */
- err[0] = 0;
- if (!id || !*id) strcpy(err, "missing id field");
- if (!rlevel) strcpy(err, "missing runlevel field");
- if (!process) strcpy(err, "missing process field");
- if (!action && !*action)
- strcpy(err, "missing action field");
- if (id && strlen(id) > sizeof(utproto.ut_id))
- sprintf(err, "id field too long (max %d characters)",
- sizeof(utproto.ut_id));
- if (rlevel && strlen(rlevel) > 11)
- strcpy(err, "rlevel field too long (max 11 characters)");
- if (process && strlen(process) > 127) strcpy(err, "process field too long");
- if (err[0] != 0) {
- Log(L_VB, "%s[%d]: %s", INITTAB, lineNo, err);
- #if DEBUG
- Log(L_VB, "%s:%s:%s:%s", id, rlevel, action, process);
- #endif
- continue;
- }
-
- /* Decode the "action" field */
- actionNo = -1;
- for(f = 0; actions[f].name; f++)
- if (strcasecmp(action, actions[f].name) == 0) {
- actionNo = actions[f].act;
- break;
- }
- if (actionNo == -1) {
- Log(L_VB, "%s[%d]: %s: unknown action field",
- INITTAB, lineNo, action);
- continue;
- }
-
- /* See if the id field is unique */
- for(old = newFamily; old; old = old->next) {
- if(strcmp(old->id, id) == 0 && strcmp(id, "~~")) {
- Log(L_VB, "%s[%d]: duplicate ID field \"%s\"",
- INITTAB, lineNo, id);
- break;
- }
- }
- if (old) continue;
-
- /* Allocate a CHILD structure */
- if ((ch = malloc(sizeof(CHILD))) == NULL) {
- Log(L_VB, "out of memory");
- continue;
- }
- memset(ch, 0, sizeof(CHILD));
-
- /* And fill it in. */
- ch->action = actionNo;
- strncpy(ch->id, id, sizeof(utproto.ut_id) + 1); /* Hack for different libs. */
- strncpy(ch->process, process, 127);
- if (rlevel[0]) {
- for(f = 0; f < 11 && rlevel[f]; f++) {
- ch->rlevel[f] = rlevel[f];
- if (ch->rlevel[f] == 's') ch->rlevel[f] = 'S';
- }
- strncpy(ch->rlevel, rlevel, 11);
- } else {
- strcpy(ch->rlevel, "0123456789");
- if (ISPOWER(ch->action))
- strcpy(ch->rlevel, "S0123456789");
- }
- /*
- * We have the fake runlevel '#' for SYSINIT and
- * '*' for BOOT and BOOTWAIT.
- */
- if (ch->action == SYSINIT) strcpy(ch->rlevel, "#");
- if (ch->action == BOOT || ch->action == BOOTWAIT)
- strcpy(ch->rlevel, "*");
-
- /* Now add it to the linked list. Special for powerfail. */
- if (ISPOWER(ch->action)) {
-
- /* Disable by default */
- ch->flags |= XECUTED;
-
- /* Tricky: insert at the front of the list.. */
- old = NULL;
- for(i = newFamily; i; i = i->next) {
- if (!ISPOWER(i->action)) break;
- old = i;
- }
- /* Now add after entry "old" */
- if (old) {
- ch->next = i;
- old->next = ch;
- if (i == NULL) head = ch;
- } else {
- ch->next = newFamily;
- newFamily = ch;
- if (ch->next == NULL) head = ch;
- }
- } else {
- /* Just add at end of the list */
- if (ch->action == KBREQUEST) ch->flags |= XECUTED;
- ch->next = NULL;
- if (head)
- head->next = ch;
- else
- newFamily = ch;
- head = ch;
- }
-
- /* Walk through the old list comparing id fields */
- for(old = family; old; old = old->next)
- if (strcmp(old->id, ch->id) == 0) {
- old->new = ch;
- break;
- }
- }
- /* We're done. */
- if (fp) fclose(fp);
-
- /*
- * Loop through the list of children, and see if they need to
- * be killed.
- */
-
- #if DEBUG
- Log(L_VB, "Checking for children to kill");
- #endif
- for(round = 0; round < 2; round++) {
- talk = 1;
- for(ch = family; ch; ch = ch->next) {
- ch->flags &= ~KILLME;
-
- /* Is this line deleted? */
- if (ch->new == NULL) ch->flags |= KILLME;
-
- #if 0 /* XXX - This is more compatible with real sysv. */
- /* See if the entry changed. Yes: kill anyway */
- if (ch->new && (strcmp(ch->process, ch->new->process) ||
- ch->action != ch->new->action)) ch->flags |= KILLME;
- #endif
-
- /* Only BOOT processes may live in all levels */
- if (ch->action != BOOT &&
- strchr(ch->rlevel, runlevel) == NULL) {
- /* Ondemand procedures live always except in single user */
- if (runlevel == 'S' || !(ch->flags & DEMAND))
- ch->flags |= KILLME;
- }
-
- /* Now, if this process may live note so in the new list */
- if ((ch->flags & KILLME) == 0) {
- ch->new->flags = ch->flags;
- ch->new->pid = ch->pid;
- ch->new->exstat = ch->exstat;
- continue;
- }
-
-
- /* Is this process still around? */
- if ((ch->flags & RUNNING) == 0) {
- ch->flags &= ~KILLME;
- continue;
- }
- #if DEBUG
- Log(L_VB, "Killing \"%s\"", ch->process);
- #endif
- switch(round) {
- case 0: /* Send TERM signal */
- if (talk)
- Log(L_CO, "Sending processes the TERM signal");
- kill(-(ch->pid), SIGTERM);
- foundOne = 1;
- break;
- case 1: /* Send KILL signal and collect status */
- if (talk)
- Log(L_CO, "Sending processes the KILL signal");
- kill(-(ch->pid), SIGKILL);
- break;
- }
- talk = 0;
-
- /* And process the next member of our family */
- }
- /* See if we have to wait 5 seconds */
- if (foundOne && round == 0) {
- /* Yup, but check every second if we still have children. */
- for(f = 0; f < sltime; f++) {
- for(ch = family; ch; ch = ch->next) {
- if (!(ch->flags & KILLME)) continue;
- if ((ch->flags & RUNNING) && !(ch->flags & ZOMBIE))
- break;
- }
- if (ch == NULL) {
- /* No running children, skip SIGKILL */
- round = 1;
- foundOne = 0; /* Skip the sleep below. */
- break;
- }
- Sleep(1);
- }
- }
- }
-
- /* Now give all processes the chance to die and collect exit statuses */
- /* FIXME: how to give away time slice?? */
- if (foundOne) Sleep(1);
- for(ch = family; ch; ch = ch->next)
- if (ch->flags & KILLME) {
- if (!(ch->flags & ZOMBIE))
- Log(L_CO, "Pid %d [id %s] seems to hang", ch->pid,
- ch->id);
- else {
- #if DEBUG
- Log(L_VB, "Updating utmp for pid %d [id %s]", ch->pid, ch->id);
- #endif
- ch->flags &= ~RUNNING;
- if (ch->process[0] != '+')
- Wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
- }
- }
-
- /* Both rounds done; clean up the list. */
- omask = siggetmask();
- sigblock(sigmask(SIGCHLD));
- for(ch = family; ch; ch = old) {
- old = ch->next;
- free(ch);
- }
- family = newFamily;
- for(ch = family; ch; ch = ch->next) ch->new = NULL;
- newFamily = NULL;
- sigsetmask(omask);
-
- /* Dispose of INITLVL file. */
- if (lstat(INITLVL, &st) >= 0 && S_ISLNK(st.st_mode)) {
- /* INITLVL is a symbolic link, so just truncate the file. */
- close(open(INITLVL, O_WRONLY|O_TRUNC));
- } else {
- /* Delete INITLVL file. */
- unlink(INITLVL);
- }
- }
-
- /*
- * Walk through the family list and start up children.
- * The entries that do not belong here at all are removed
- * from the list.
- */
- void StartEmIfNeeded(void)
- {
- CHILD *ch; /* Pointer to child */
- int delete; /* Delete this entry from list? */
-
- #if DEBUG
- Log(L_VB, "Checking for children to start");
- #endif
-
- for(ch = family; ch; ch = ch->next) {
-
- #if DEBUG
- if (ch->rlevel[0] == 'C') {
- Log(L_VB, "%s: flags %d", ch->process, ch->flags);
- }
- #endif
-
- /* Are we waiting for this process? Then quit here. */
- if (ch->flags & WAITING) break;
-
- /* Already running? OK, don't touch it */
- if (ch->flags & RUNNING) continue;
-
- /* See if we have to start it up */
- delete = 1;
- if (strchr(ch->rlevel, runlevel) ||
- ((ch->flags & DEMAND) && !strchr("#*Ss", runlevel))) {
- StartUp(ch);
- delete = 0;
- }
-
- if (delete) {
- /* FIXME: is this OK? */
- ch->flags &= ~(RUNNING|WAITING);
- if (!ISPOWER(ch->action) && ch->action != KBREQUEST)
- ch->flags &= ~XECUTED;
- ch->pid = 0;
- } else
- /* Do we have to wait for this process? */
- if (ch->flags & WAITING) break;
- }
- /* Done. */
- }
-
- /*
- * Ask the user on the console for a runlevel
- */
- int AskRunLevel()
- {
- int lvl = -1;
- char buf[8];
- int fd;
-
-
- SetTerm(0);
- fd = serial_open(console_dev, O_RDWR|O_NOCTTY);
- /* FIXME as soon as O_NDELAY != O_NONBLOCK */
- if (fd < 0) return('S');
-
- while(!strchr("0123456789S", lvl)) {
- write(fd, "\nEnter runlevel: ", 17);
- buf[0] = 0;
- read(fd, buf, 8);
- if (buf[0] != 0 && (buf[1] == '\r' || buf[1] == '\n')) lvl = buf[0];
- if (islower(lvl)) lvl = toupper(lvl);
- }
- close(fd);
- return(lvl);
- }
-
- /*
- * Search the INITTAB file for the 'initdefault' field, with the default
- * runlevel. If this fails, ask the user to supply a runlevel.
- */
- int GetInitDefault(void)
- {
- CHILD *ch;
- int lvl = -1;
- char *p;
-
- /* Look for initdefault. */
- for(ch = family; ch; ch = ch->next)
- if (ch->action == INITDEFAULT) {
- p = ch->rlevel;
- while(*p) {
- if (*p > lvl) lvl = *p;
- p++;
- }
- break;
- }
- /* See if level is valid */
- if (lvl > 0) {
- if (islower(lvl)) lvl = toupper(lvl);
- if (strchr("0123456789S", lvl) == NULL) {
- Log(L_VB, "Initdefault level '%c' is invalid", lvl);
- lvl = 0;
- }
- }
- /* Ask for runlevel on console if needed. */
- if (lvl <= 0) lvl = AskRunLevel();
-
- /* Log the fact that we have a runlevel now. */
- return(lvl);
- }
-
-
- /*
- * We got signaled: read the new level from INITLVL
- */
- int ReadLevel(int arg)
- {
- unsigned char foo = 'X'; /* Contents of INITLVL */
- int st; /* Sleep time */
- CHILD *ch; /* Walk through list */
- FILE *fp; /* File pointer for INITLVL */
- int ok = 0;
- struct stat stt;
-
- #if CLEAN_UTMP
- /* Clean up utmp file first. */
- Wtmp(NULL, "", -2, 0, NULL);
- #endif
-
- if (arg == 0) {
- if ((fp = fopen(INITLVL, "r")) == NULL ||
- (stat(INITLVL, &stt) == 0 && stt.st_size == 0L)) {
- /* INITLVL file is empty or not there - act as 'init q' */
- Log(L_SY, "Re-reading inittab");
- return(runlevel);
- }
- ok = fscanf(fp, "%c %d", &foo, &st);
- fclose(fp);
- } else {
- /* We go to the new runlevel passed as an argument. */
- foo = arg;
- ok = 1;
- }
- if (islower(foo)) foo = toupper(foo);
-
- if (ok < 1 || ok > 2 || strchr("QS0123456789ABC", foo) == NULL) {
- Log(L_VB, "bad runlevel: %c", foo);
- return(runlevel);
- }
- if (ok == 2) sltime = st;
-
- /* Be verbose 'bout it all */
- switch(foo) {
- case 'S':
- Log(L_VB, "Going single user");
- break;
- case 'Q':
- Log(L_SY, "Re-reading inittab");
- break;
- case 'A':
- case 'B':
- case 'C':
- Log(L_SY, "Activating demand-procedures for '%c'", foo);
- break;
- default:
- Log(L_VB, "Switching to runlevel: %c", foo);
- }
-
- if (foo == 'Q') return(runlevel);
-
- /* Check if this is a runlevel a, b or c */
- if (strchr("ABC", foo)) {
- if (runlevel == 'S') return(runlevel);
-
- /* Read inittab again first! */
- ReadItab();
-
- /* Mark those special tasks */
- for(ch = family; ch; ch = ch->next)
- if (strchr(ch->rlevel, foo) != NULL ||
- strchr(ch->rlevel, tolower(foo)) != NULL) {
- ch->flags |= DEMAND;
- ch->flags &= ~XECUTED;
- #if DEBUG
- Log(L_VB, "Marking (%s) as ondemand, flags %d",
- ch->id, ch->flags);
- #endif
- }
- return(runlevel);
- }
-
- /* Store both the old and the new runlevel. */
- Wtmp("runlevel", "~~", foo + 256*runlevel, RUN_LVL, "~");
- thislevel = foo;
- prevlevel = runlevel;
- return(foo);
- }
-
-
- /*
- * Zero out an utmp struct but copy the id field.
- */
- void EmptyStruct(struct utmp *oldu, struct utmp *newu)
- {
- memset(newu, 0, sizeof(struct utmp));
- strncpy(newu->ut_id, oldu->ut_id, sizeof(newu->ut_id));
- strncpy(newu->ut_line, oldu->ut_line, sizeof(newu->ut_line));
- newu->ut_type = DEAD_PROCESS;
- }
-
- /*
- * Log an event ONLY in the wtmp file (reboot, runlevel)
- */
- void WtmpOnly(
- char *user, /* name of user */
- char *id, /* inittab ID */
- int pid, /* PID of process */
- int type, /* TYPE of entry */
- char *line) /* Which line is this */
- {
- int fd;
- struct utmp utmp;
- time_t t;
-
- if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND)) < 0) return;
-
- /* Note if we are going to write a boot record. */
- if (type == BOOT_TIME) wroteReboot++;
-
- /*
- * See if we need to write a reboot record. The reason that
- * we are being so paranoid is that when we first tried to
- * write the reboot record, /var was possibly not mounted
- * yet. As soon as we can open WTMP we write a delayed boot record.
- */
- if (wroteReboot == 0 && type != BOOT_TIME)
- WtmpOnly("reboot", "~~", 0, BOOT_TIME, "~");
-
- /* Zero the fields */
- memset(&utmp, 0, sizeof(utmp));
-
- /* Enter new fields */
- time(&t);
- utmp.ut_time = t;
- utmp.ut_pid = pid;
- utmp.ut_type = type;
- strncpy(utmp.ut_name, user, sizeof(utmp.ut_name));
- strncpy(utmp.ut_id , id , sizeof(utmp.ut_id ));
- strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
-
- write(fd, (char *)&utmp, sizeof(utmp));
- close(fd);
- }
-
- /*
- * Log an event into the WTMP and UTMP files.
- *
- * FIXME: we shouldn't zero out other processes' utmp entries. What we
- * should do to get/update an utmp entry:
- * o see if an entry with matching ut_id field is present
- * o if so, re-use it.
- * o if not, re-open utmp in O_APPEND mode and add new entry.
- *
- * Thanks to Michael Haardt for pointing this out.
- *
- * Also, check if the RUNLEVEL entry is still present and rewrite if
- * it is missing. (People tend to mess up their utmp files).
- *
- */
- void Wtmp(
- char *user, /* name of user */
- char *id, /* inittab ID */
- int pid, /* PID of process */
- int type, /* TYPE of entry */
- char *line) /* LINE if used. */
- {
- struct utmp utmp; /* UTMP/WTMP User Accounting */
- struct utmp tmp; /* Scratch */
- struct utmp empty; /* Empty utmp struct */
- int fd = -1; /* File Descriptor for UTMP */
- int fd2; /* File Descriptor for WTMP */
- int found = 0; /* Was the record found in UTMP */
- int freeEntry = -1; /* Was a free entry found during UTMP scan? */
- int matchEntry = -1; /* Was a matching entry found ? */
- int lineno; /* Offset into UTMP file */
- time_t t; /* What's the time? */
- static int opened = 0; /* Already opened /var/run/utmp? */
- #if CLEAN_UTMP
- CHILD *ch; /* Scratch */
- #endif
-
- /* Clear utmp if not yet opened. */
- if (!opened) close(open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644));
-
- /* First read the utmp entry for this process */
- if ((fd = open(UTMP_FILE, O_RDWR)) >= 0) {
- lineno = 0;
- opened = 1;
- while (read(fd, (char *) &tmp, sizeof(tmp)) == sizeof(tmp)) {
- #if DEBUG
- Log(L_VB, "utmp #%d: pid %d, type %d", lineno / sizeof(tmp),
- tmp.ut_pid, tmp.ut_type);
- #endif
- if (user != NULL &&
- ((tmp.ut_pid == pid && tmp.ut_type != NO_PROCESS) ||
- (tmp.ut_type == RUN_LVL && type == RUN_LVL))) {
- found = 1;
- memcpy(&utmp, &tmp, sizeof(utmp));
- if (type == DEAD_PROCESS) {
- /* Zero all entries with this pid */
- (void) lseek(fd, (long) lineno, SEEK_SET);
- EmptyStruct(&tmp, &empty);
- write(fd, (char *)&empty, sizeof(struct utmp));
- } else {
- (void) lseek(fd, (long) lineno, SEEK_SET);
- break;
- }
- }
- #if CLEAN_UTMP
- /* Let's check if this process still exists. If it
- * doesn't, clear it's utmp entry.
- */
- else if (tmp.ut_type != NO_PROCESS &&
- tmp.ut_type != RUN_LVL && tmp.ut_pid > 0) {
- /* One of our children? */
- for(ch = family; ch; ch = ch->next)
- if (ch->pid == tmp.ut_pid) break;
- if (ch == NULL && kill(tmp.ut_pid, 0) < 0) {
- #if DEBUG
- Log(L_VB, "utmp: cleaning up stale entry (pid %d)",
- tmp.ut_pid);
- #endif
- /* Zero the entry with this pid */
- (void) lseek(fd, (long) lineno, SEEK_SET);
- EmptyStruct(&tmp, &empty);
- if (write(fd, (char *)&empty, sizeof(struct utmp))
- != sizeof(struct utmp)) {
- Log(L_VB, "error writing utmp struct");
- break;
- }
- }
- }
- #endif
- /* See if this is a unused entry, save it for later */
- if ((tmp.ut_pid == 0 && tmp.ut_id[0] == 0
- && tmp.ut_user[0] == 0 && tmp.ut_type != RUN_LVL) || tmp.ut_type == 0)
- if (freeEntry < 0) freeEntry = lineno;
-
- /* See if this entry is dead and matches our "id", save it for later. */
- if (tmp.ut_type == DEAD_PROCESS && strncmp(tmp.ut_id, id, sizeof(tmp.ut_id)) == 0
- && tmp.ut_user[0] == 0)
- if (matchEntry < 0) matchEntry = lineno;
-
- lineno += sizeof(tmp);
- }
- #if CLEAN_UTMP
- /* Only a call to clean the utmp file? */
- if (user == NULL) {
- (void) close(fd);
- return;
- }
- #endif
- }
-
- #if DEBUG
- Log(L_VB, "Wtmp(): found=%d, match=%d, free=%d", found, matchEntry, freeEntry);
- #endif
-
- if (!found) { /* Enter some defaults */
- /* Zero the fields */
- memset(&utmp, 0, sizeof(utmp));
-
- /* Enter new fields */
- strncpy(utmp.ut_name, user, sizeof(utmp.ut_name));
- strncpy(utmp.ut_id , id , sizeof(utmp.ut_id ));
- strcpy (utmp.ut_line, "");
-
- /* Where to write new utmp record */
- if (matchEntry >= 0) freeEntry = matchEntry;
- if (freeEntry >= 0)
- lseek(fd, (long) freeEntry, SEEK_SET);
- }
-
- /* Change the values of some fields */
- time(&t);
- utmp.ut_type = type;
- utmp.ut_time = t;
- utmp.ut_pid = pid;
- if (line) strncpy(utmp.ut_line, line, sizeof(utmp.ut_line));
-
- /* Write the utmp record, if needed */
- if (fd >= 0) {
- /* DEAD_PROCESS has already been zeroed */
- if (utmp.ut_type != DEAD_PROCESS)
- (void) write(fd, (char *) &utmp, sizeof(struct utmp));
- (void) close(fd);
- }
-
- /* Write the wtmp record */
- if ((fd2 = open(WTMP_FILE, O_WRONLY|O_APPEND)) >= 0) {
- /* See if we need to write a boot record */
- if (wroteReboot == 0 && type != BOOT_TIME) {
- WtmpOnly("reboot", "~~", 0, BOOT_TIME, "~");
- wroteReboot++;
- }
- /* Set ut_user to 0 if this is a logout record */
- if (utmp.ut_type == DEAD_PROCESS) utmp.ut_name[0] = 0;
- (void) write(fd2, (char *) &utmp, sizeof(struct utmp));
- (void) close(fd2);
- }
- }
-
- /*
- * This procedure is called after every signal (SIGHUP, SIGALRM..)
- *
- * Only clear the 'failing' flag if the process is sleeping
- * longer than 5 minutes, or inittab was read again due
- * to user interaction.
- */
- void FailCheck(void)
- {
- time_t t; /* System time */
- CHILD *ch; /* Pointer to child structure */
- time_t nxtAlrm = 0; /* When to set next alarm */
-
- time(&t);
-
- for(ch = family; ch; ch = ch->next) {
-
- if (ch->flags & FAILING) {
- /* Can we free this sucker? */
- if (ch->tm + SLEEPTIME < t) {
- ch->flags &= ~FAILING;
- ch->count = 0;
- ch->tm = 0;
- } else {
- /* No, we'll look again later */
- if (nxtAlrm == 0 || ch->tm + SLEEPTIME > nxtAlrm)
- nxtAlrm = ch->tm + SLEEPTIME;
- }
- }
- }
- if (nxtAlrm) {
- nxtAlrm -= t;
- if (nxtAlrm < 1) nxtAlrm = 1;
- alarm(nxtAlrm);
- }
- }
-
- /* Set all 'Fail' timers to 0 */
- void FailCancel(void)
- {
- CHILD *ch;
-
- for(ch = family; ch; ch = ch->next) {
- ch->count = 0;
- ch->tm = 0;
- ch->flags &= ~FAILING;
- }
- }
-
- /* Start up powerfail entries. */
- void DoPowerFail(int pwrstat)
- {
- CHILD *ch;
-
- /* Tell powerwait & powerfail entries to start up */
- for(ch = family; ch; ch = ch->next) {
- if (pwrstat == 'O') {
- /* The power is good again. */
- if (ch->action == POWEROKWAIT)
- ch->flags &= ~XECUTED;
- } else if (pwrstat == 'L') {
- /* Low battery, shut down now. */
- if (ch->action == POWERFAILNOW)
- ch->flags &= ~XECUTED;
- } else {
- /* Power is failing */
- if (ch->action == POWERFAIL || ch->action == POWERWAIT)
- ch->flags &= ~XECUTED;
- }
- }
- }
-
- #if NEWINIT
-
- /*
- * We got a change runlevel request through the
- * init.fifo. Process it.
- */
- void FifoNewLevel(int level)
- {
- int oldlevel;
- #if CHANGE_WAIT
- CHILD *ch;
- #endif
-
- if (level == runlevel) return;
-
- #if CHANGE_WAIT
- /* Are we waiting for a child? */
- for(ch = family; ch; ch = ch->next)
- if (ch->flags & WAITING) break;
- if (ch == NULL)
- #endif
- {
- /* We need to go into a new runlevel */
- oldlevel = runlevel;
- runlevel = ReadLevel(level);
- if (oldlevel != 'S' && runlevel == 'S') SetTerm(0);
- if (runlevel == '6' || runlevel == '0' || runlevel == '1') SetTerm(0);
- ReadItab();
- FailCancel();
- setproctitle("init [%c]", runlevel);
- }
- }
-
- /*
- * Read from the init FIFO. Processes like telnetd and rlogind can
- * ask us to create login processes on their behalf.
- *
- * FIXME: this needs to be finished.
- */
- void CheckInitFifo(void)
- {
- static int pipe_fd = -1;
- struct init_request request;
- int n;
- fd_set fds;
- int quit = 0;
-
- /* Try to open /dev/initctl */
- if (pipe_fd < 0) {
- if ((pipe_fd = open(INIT_FIFO, O_RDWR|O_NONBLOCK)) >= 0) {
- /* Don't use fd's 0, 1 or 2. */
- (void) dup2(pipe_fd, PIPE_FD);
- close(pipe_fd);
- pipe_fd = PIPE_FD;
- }
- }
-
- /* Wait for data to appear, _if_ the pipe was opened. */
- if (pipe_fd >= 0) while(!quit) {
-
- /* Do select, return on EINTR. */
- FD_ZERO(&fds);
- FD_SET(pipe_fd, &fds);
- n = select(pipe_fd + 1, &fds, NULL, NULL, NULL);
- if (n <= 0) {
- if (errno == EINTR) return;
- continue;
- }
-
- /* Read the data, return on EINTR. */
- n = read(pipe_fd, &request, sizeof(request));
- if (n == 0) {
- /*
- * End of file. This can't happen under Linux (because
- * the pipe is opened O_RDWR - see select() in the
- * kernel) but you never know...
- */
- close(pipe_fd);
- pipe_fd = -1;
- return;
- }
- if (n <= 0) {
- if (errno == EINTR) return;
- Log(L_VB, "error reading initrequest");
- continue;
- }
-
- /* Process request. */
- if (request.magic != INIT_MAGIC || n != sizeof(request)) {
- Log(L_VB, "got bogus initrequest");
- continue;
- }
- switch(request.cmd) {
- case INIT_CMD_RUNLVL:
- sltime = request.sleeptime;
- FifoNewLevel(request.runlevel);
- quit = 1;
- break;
- case INIT_CMD_POWERFAIL:
- sltime = request.sleeptime;
- DoPowerFail('F');
- quit = 1;
- break;
- case INIT_CMD_POWERFAILNOW:
- sltime = request.sleeptime;
- DoPowerFail('L');
- quit = 1;
- break;
- case INIT_CMD_POWEROK:
- sltime = request.sleeptime;
- DoPowerFail('O');
- quit = 1;
- break;
- default:
- Log(L_VB, "got unimplemented initrequest.");
- break;
- }
- }
-
- /* We come here if the pipe couldn't be opened. */
- if (pipe_fd < 0) pause();
-
- }
- #endif
-
-
- /*
- * This function is used in the transition
- * sysinit (-> single user) boot -> multi-user.
- */
- void BootTransitions()
- {
- CHILD *ch;
- static int did_boot = 0;
- static int newlevel = 0;
- static int loglevel;
- static int oldlevel;
-
- /* Check if there is something to wait for! */
- for( ch = family; ch; ch = ch->next )
- if ((ch->flags & RUNNING) && ch->action != BOOT) break;
-
- if (ch == NULL) {
- /* No processes left in this level, proceed to next level. */
- loglevel = -1;
- oldlevel = 'N';
- switch(runlevel) {
- case '#': /* SYSINIT -> BOOT */
- #if DEBUG
- Log(L_VB, "SYSINIT -> BOOT");
- #endif
- /* Write a boot record. */
- wroteReboot = 0;
- WtmpOnly("reboot", "~~", 0, BOOT_TIME, "~");
-
- /* Get our run level */
- newlevel = dflLevel ? dflLevel : GetInitDefault();
- if (newlevel == 'S') {
- runlevel = newlevel;
- /* Not really 'S' but show anyway. */
- setproctitle("init [S]");
- } else
- runlevel = '*';
- break;
- case '*': /* BOOT -> NORMAL */
- #if DEBUG
- Log(L_VB, "BOOT -> NORMAL");
- #endif
- if (runlevel != newlevel)
- loglevel = newlevel;
- runlevel = newlevel;
- did_boot = 1;
- break;
- case 'S': /* Ended SU mode */
- case 's':
- #if DEBUG
- Log(L_VB, "END SU MODE");
- #endif
- /* Save tty modes. */
- SetTerm(1);
- newlevel = GetInitDefault();
- if (!did_boot && newlevel != 'S')
- runlevel = '*';
- else {
- if (runlevel != newlevel)
- loglevel = newlevel;
- runlevel = newlevel;
- oldlevel = 'S';
- }
- for(ch = family; ch; ch = ch->next)
- if (strcmp(ch->rlevel, "S") == 0)
- ch->flags &= ~(FAILING|WAITING|XECUTED);
- break;
- default:
- loglevel = -1;
- Log(L_VB, "no more processes left in this runlevel");
- #if NEWINIT
- CheckInitFifo();
- #else
- pause();
- #endif
- break;
- }
- if (loglevel > 0) {
- Log(L_VB, "Entering runlevel: %c", runlevel);
- Wtmp("runlevel", "~~", runlevel + 256 * oldlevel, RUN_LVL, "~");
- thislevel = runlevel;
- prevlevel = oldlevel;
- setproctitle("init [%c]", runlevel);
- }
- }
- }
-
- /*
- * Init got hit by a signal. See which signal it is,
- * and act accordingly.
- */
- void ProcessSignals()
- {
- int pwrstat;
- int oldlevel;
- int fd;
- CHILD *ch;
- char c;
-
- if (sigismember(&got_signals, SIGPWR)) {
- #if DEBUG
- Log(L_VB, "got SIGPWR");
- #endif
- /* See _what_ kind of SIGPWR this is. */
- pwrstat = 0;
- if ((fd = open(PWRSTAT, O_RDONLY)) >= 0) {
- c = 0;
- read(fd, &c, 1);
- pwrstat = c;
- close(fd);
- unlink(PWRSTAT);
- DoPowerFail(pwrstat);
- }
- sigdelset(&got_signals, SIGPWR);
- }
-
- if (sigismember(&got_signals, SIGINT)) {
- #if DEBUG
- Log(L_VB, "got SIGINT");
- #endif
- /* Tell ctrlaltdel entry to start up */
- for(ch = family; ch; ch = ch->next)
- if (ch->action == CTRLALTDEL)
- ch->flags &= ~XECUTED;
- sigdelset(&got_signals, SIGINT);
- }
-
- if (sigismember(&got_signals, SIGWINCH)) {
- #if DEBUG
- Log(L_VB, "got SIGWINCH");
- #endif
- /* Tell kbrequest entry to start up */
- for(ch = family; ch; ch = ch->next)
- if (ch->action == KBREQUEST)
- ch->flags &= ~XECUTED;
- sigdelset(&got_signals, SIGWINCH);
- }
-
- if (sigismember(&got_signals, SIGALRM)) {
- #if DEBUG
- Log(L_VB, "got SIGALRM");
- #endif
- /* The timer went off: check it out */
- sigdelset(&got_signals, SIGALRM);
- }
-
- if (sigismember(&got_signals, SIGCHLD)) {
- #if DEBUG
- Log(L_VB, "got SIGCHLD");
- #endif
- /* First set flag to 0 */
- sigdelset(&got_signals, SIGCHLD);
-
- /* See which child this was */
- for(ch = family; ch; ch = ch->next)
- if (ch->flags & ZOMBIE) {
- #if DEBUG
- Log(L_VB, "Child died, PID= %d", ch->pid);
- #endif
- ch->flags &= ~(RUNNING|ZOMBIE|WAITING);
- if (ch->process[0] != '+')
- Wtmp("", ch->id, ch->pid, DEAD_PROCESS, NULL);
- }
-
- }
-
- if (sigismember(&got_signals, SIGHUP)) {
- #if DEBUG
- Log(L_VB, "got SIGHUP");
- #endif
- #if CHANGE_WAIT
- /* Are we waiting for a child? */
- for(ch = family; ch; ch = ch->next)
- if (ch->flags & WAITING) break;
- if (ch == NULL)
- #endif
- {
- /* We need to go into a new runlevel */
- oldlevel = runlevel;
- runlevel = ReadLevel(0);
- if (oldlevel != 'S' && runlevel == 'S') SetTerm(0);
- if (runlevel == '6' || runlevel == '0' || runlevel == '1') SetTerm(0);
- ReadItab();
- FailCancel();
- setproctitle("init [%c]", runlevel);
- sigdelset(&got_signals, SIGHUP);
- }
- }
- }
-
- /*
- * The main loop
- */
- int InitMain()
- {
- int f, st;
- CHILD *ch;
- struct sigaction sa;
- char *s;
-
- #if INITDEBUG
- /*
- * Fork so we can debug the init process.
- */
- if ((f = fork()) > 0) {
- while(wait(&st) != f)
- ;
- write(1, "PRNT: init killed.\r\n", 20);
- while(1) pause();
- }
- #endif
-
- #ifdef ROOTFS
- /* Close all file descriptors. */
- close(0);
- close(1);
- close(2);
-
- /* Mount new root file system on /root */
- if (mount(ROOTFS, "/root", "ext2", 0, 0) < 0) {
- Log(L_VB, "mount(%s, /root): %s", ROOTFS, sys_errlist[errno]);
- while(1) pause();
- }
-
- /* Now do the chroot (needs kernel patch). */
- if (chroot("/root") < 0) {
- Log(L_VB, "chroot(/root): %s", sys_errlist[errno]);
- while(1) pause();
- }
- #endif
-
- /* Tell the kernel to send us SIGINT when CTRL-ALT-DEL is pressed */
- reboot(0xfee1dead, 672274793, 0);
-
- /* Tell the kernel that we want to handle keyboard signals. */
- #ifdef KDSIGACCEPT
- if ((f = open(VT_MASTER, O_RDWR | O_NOCTTY)) >= 0) {
- (void) ioctl(f, KDSIGACCEPT, SIGWINCH);
- close(f);
- } else
- (void) ioctl(0, KDSIGACCEPT, SIGWINCH);
- #endif
-
- /* Set up signals */
- for(f = 1; f <= _NSIG; f++)
- signal(f, SIG_IGN);
-
- SETSIG(sa, SIGALRM, signal_handler);
- SETSIG(sa, SIGHUP, signal_handler);
- SETSIG(sa, SIGINT, signal_handler);
- SETSIG(sa, SIGPWR, signal_handler);
- SETSIG(sa, SIGWINCH, signal_handler);
- SETSIG(sa, SIGSTOP, stop_handler);
- SETSIG(sa, SIGTSTP, stop_handler);
- SETSIG(sa, SIGCONT, cont_handler);
- SETSIG(sa, SIGSEGV, segv_handler);
-
- /* Close whatever files are open, and reset the console. */
- close(0);
- close(1);
- close(2);
- if ((s = getenv("CONSOLE")) != NULL)
- console_dev = s;
- SetTerm(0);
- setsid();
-
- /* Set default PATH variable (for ksh) */
- if (getenv("PATH") == NULL) putenv(PATH_DFL);
-
- /* Initialize /var/run/utmp (only works if /var is on root and mounted rw) */
- (void) close(open(UTMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644));
-
- /* Say hello to the world */
- Log(L_CO, bootmsg);
-
- /* See if we have to start an emergency shell. */
- if (emerg_shell) {
- SETSIG(sa, SIGCHLD, SIG_DFL);
- if ((f = Spawn(&ch_emerg)) > 0) {
- while(wait(&st) != f)
- ;
- }
- }
- SETSIG(sa, SIGCHLD, chld_handler);
-
- /* Start normal boot procedure. */
- runlevel = '#';
- ReadItab();
- StartEmIfNeeded();
-
- while(1) {
-
- /* See if we need to make the boot transitions. */
- BootTransitions();
- #if DEBUG
- Log(L_VB, "InitMain: waiting..");
- #endif
-
- /* Check if there are processes to be waited on. */
- for(ch = family; ch; ch = ch->next)
- if ((ch->flags & RUNNING) && ch->action != BOOT) break;
- #if 0
- if (ch == NULL && got_signals == 0)
- Log(L_VB, "no more processes in this runlevel");
- #endif
-
- #if CHANGE_WAIT
- /* Wait until we get hit by some signal. */
- while (ch != NULL && got_signals == 0) {
- if (sigismember(&got_signals, SIGHUP)) {
- /* See if there are processes to be waited on. */
- for(ch = family; ch; ch = ch->next)
- if (ch->flags & WAITING) break;
- }
- # if NEWINIT
- if (ch != NULL) CheckInitFifo();
- # else
- if (ch != NULL) pause();
- # endif
- }
- #else /* CHANGE_WAIT */
- # if NEWINIT
- if (ch != NULL && got_signals == 0) CheckInitFifo();
- # else
- if (ch != NULL && got_signals == 0) pause();
- # endif
- #endif /* CHANGE_WAIT */
-
- /* Check the 'failing' flags */
- FailCheck();
-
- /* Process any signals. */
- ProcessSignals();
-
- /* See what we need to start up (again) */
- StartEmIfNeeded();
- sync();
- }
- /*NOTREACHED*/
- }
-
- /*
- * Tell the user about the syntax we expect.
- */
- void Usage(char *s)
- {
- fprintf(stderr, "Usage: %s 0123456SsQqAaBbCc\n", s);
- exit(1);
- }
-
- /*
- * Main entry for init and telinit.
- */
- int main(int argc, char *argv[])
- {
- #if NEWINIT
- struct init_request request;
- #endif
- char *p;
- FILE *fp;
- int f, fd;
-
- /* Get my own name */
- if ((p = strrchr(argv[0], '/')) != NULL)
- p++;
- else
- p = argv[0];
-
-
- /* See if I want to become "father of all processes" */
- #ifndef TEST
- if (getpid() == INITPID || strcmp(p, "init.new") == 0 || strcmp(p, "sh") == 0) {
- #else
- {
- #endif
- /* Check command line arguments */
- maxproclen = strlen(argv[0]) + 1;
- for(f = 1; f < argc; f++) {
- if (!strcmp(argv[f], "single"))
- dflLevel = 'S';
- else if (!strcmp(argv[f], "-a") || !strcmp(argv[f], "auto"))
- putenv("AUTOBOOT=YES");
- else if (!strcmp(argv[f], "-b") || !strcmp(argv[f], "emergency"))
- emerg_shell = 1;
- else if (strchr("0123456789sS", argv[f][0])
- && strlen(argv[f]) == 1)
- dflLevel = argv[f][0];
- maxproclen += strlen(argv[f]) + 1;
- }
- maxproclen--;
-
- /* Start booting. */
- argv0 = argv[0];
- argv[1] = NULL;
- setproctitle("init boot");
- InitMain(dflLevel);
- }
-
- /* Nope, this is a change-run-level init */
- while((f = getopt(argc, argv, "t:")) != EOF) {
- switch(f) {
- case 't':
- sltime = atoi(optarg);
- break;
- default:
- Usage(p);
- break;
- }
- }
-
- if (geteuid() != 0) {
- fprintf(stderr, "init: must be superuser.\n");
- exit(1);
- }
-
- /* Check syntax. */
- if (argc - optind != 1 || strlen(argv[optind]) != 1) Usage(p);
- if (!strchr("0123456789SsQqAaBbCc", argv[optind][0])) Usage(p);
-
- umask(022);
-
- #if NEWINIT
- /* Open the fifo and write a command. */
- memset(&request, 0, sizeof(request));
- request.magic = INIT_MAGIC;
- request.cmd = INIT_CMD_RUNLVL;
- request.runlevel = argv[optind][0];
- request.sleeptime = sltime;
-
- /* Make sure we don't hang on opening /etc/init.fifo */
- signal(SIGALRM, signal_handler);
- alarm(3);
- if ((fd = open(INIT_FIFO, O_WRONLY)) >= 0 &&
- write(fd, &request, sizeof(request)) == sizeof(request)) {
- close(fd);
- alarm(0);
- return 0;
- }
- /* Fallthrough to the old method. */
- #endif
- /* Now write the new runlevel. */
- if ((fp = fopen(INITLVL, "w")) == NULL) {
- fprintf(stderr, "%s: cannot create %s\n", p, INITLVL);
- exit(1);
- }
- fprintf(fp, "%s %d", argv[optind], sltime);
- fclose(fp);
-
- /* And tell init about the pending runlevel change. */
- if (kill(INITPID, SIGHUP) < 0) perror(p);
-
- exit(0);
- }
-